package com.tc.zip;

import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * <p>ZIP压缩解压缩工具类</p>
 * <pre>
 *     author      XueQi
 *     date        2017/8/31
 *     email       sage.xue@vipshop.com
 * </pre>
 */
public class ZipUtils {
	/**
	 * zip压缩文件
	 *
	 * @param zipFilePath 目标文件全路径 /apps/dat/xxx.zip
	 * @param inputFile
	 * @throws Exception
	 */
	public static File compressing(String zipFilePath, File inputFile) throws Exception {
		ZipOutputStream      out = null;
		BufferedOutputStream bo  = null;
		try {
			out = new ZipOutputStream(new FileOutputStream(zipFilePath));
			bo = new BufferedOutputStream(out);
			zip(out, inputFile, inputFile.getName(), bo);
			return new File(zipFilePath);
		} catch (Exception e) {
			throw e;
		} finally {
			if (bo != null) bo.close();
			if (out != null) out.close();
		}
	}

	/**
	 * 解压缩zip文件
	 *
	 * @param unzipFilePath 解压缩文件的输出路径
	 * @param inputFile     待解压缩的文件
	 */
	public static Map<String, File> decompressing(String unzipFilePath, File inputFile) throws Exception {
		ZipInputStream      zipInput      = null;
		BufferedInputStream bufferedInput = null;
		try {
			zipInput = new ZipInputStream(new FileInputStream(inputFile),Charset.forName("gbk"));
			bufferedInput = new BufferedInputStream(zipInput);
			return unzip(zipInput, unzipFilePath, bufferedInput);
		} catch (Exception e) {
			throw e;
		} finally {
			if (bufferedInput != null) bufferedInput.close();
			if (zipInput != null) zipInput.close();
		}
	}

	//...................................................................//

	/**
	 * 压缩算法, 支持递归压缩文件夹
	 *
	 * @param out
	 * @param f
	 * @param base
	 * @param bo
	 * @throws Exception
	 */
	private static void zip(ZipOutputStream out, File f, String base,
	                        BufferedOutputStream bo) throws Exception {
		FileInputStream     in = null;
		BufferedInputStream bi = null;
		try {
			// 判断源文件是否为文件夹
			if (f.isDirectory()) {
				File[] fl = f.listFiles();
				if (fl.length == 0) {
					out.putNextEntry(new ZipEntry(base + "/")); // 创建zip压缩进入点base
				}
				for (int i = 0; i < fl.length; i++) {
					zip(out, fl[i], base + "/" + fl[i].getName(), bo); // 递归遍历子文件夹
				}
			} else {
				out.putNextEntry(new ZipEntry(base)); // 创建zip压缩进入点base
				in = new FileInputStream(f);
				bi = new BufferedInputStream(in);

				int b;
				while ((b = bi.read()) != -1) {
					bo.write(b); // 将字节流写入当前zip目录
				}
			}
		} catch (Exception e) {
			throw e;
		} finally {
			if (bi != null) bi.close();
			if (in != null) in.close();
		}
	}

	/**
	 * 解压缩zip文件
	 *
	 * @param zipInput
	 * @param unzipFilePath
	 * @param bufferedInput
	 * @return 文件名-File
	 * @throws Exception
	 */
	private static Map<String, File> unzip(ZipInputStream zipInput, String unzipFilePath, BufferedInputStream bufferedInput)
			throws Exception {
		Map<String, File> result = new HashMap<>();

		FileOutputStream     fileOutput     = null;
		BufferedOutputStream bufferedOutput = null;
		ZipEntry             entry          = null;
		try {
			while ((entry = zipInput.getNextEntry()) != null && !entry.isDirectory()) {
				File outFile = new File(unzipFilePath, entry.getName());
				if (!outFile.exists()) {
					(new File(outFile.getParent())).mkdirs();
				}

				fileOutput = new FileOutputStream(outFile);
				bufferedOutput = new BufferedOutputStream(fileOutput);
				int b;
				while ((b = bufferedInput.read()) != -1) {
					bufferedOutput.write(b);
				}

				bufferedOutput.close();
				fileOutput.close();

				result.put(entry.getName(), outFile);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (bufferedOutput != null) bufferedOutput.close();
			if (fileOutput != null) fileOutput.close();
		}

		return result;
	}

	// 4MB buffer
	private static final byte[] BUFFER = new byte[4096 * 1024];

	public static List<String> copy(String source, ZipOutputStream target) throws Exception {
		List<String> ret=new ArrayList<>();
		File file= new File(source);
		ZipFile old =null;
		if(file.exists()){
			old = new ZipFile(file);
			try {
				Enumeration<? extends ZipEntry> entries = old.entries();
				while (entries.hasMoreElements()) {
					ZipEntry e = entries.nextElement();
					target.putNextEntry(e);
					if (!e.isDirectory()) {
						copy(old.getInputStream(e), target);
					}
					target.closeEntry();
					ret.add(e.getName());
				}
			}finally {
				if(old!=null )old.close();
			}
		}
		return ret;
	}


	public static void copy(InputStream input, OutputStream output) throws IOException {
		int bytesRead;
		while ((bytesRead = input.read(BUFFER))!= -1) {
			output.write(BUFFER, 0, bytesRead);
		}
	}

}
